import json
import uuid
from dataclasses import dataclass
from typing import Any
from uuid import UUID

from injector import inject
from sqlalchemy import desc

from internal.core.tools.api_tools.entities import APIToolsBean, ToolEntity
from internal.exception import ValidateErrorException
from internal.model import ApiToolProvider, ApiTool
from internal.schema import CreateApiToolSchema
from internal.schema.api_tool_schema import GetApiToolProvidersWithPageReq
from pkg.pageinator import Pageinator
from pkg.sqlalchemy import SQLAlchemy
from .base_service import BaseService
from internal.core.tools.api_tools.provider import ApiProviderManager


@inject
@dataclass
class APIToolService(BaseService):
    db: SQLAlchemy
    api_provider_manager: ApiProviderManager

    def validator_schema(self, schema_bean: str):
        """解析传递的openapi_schema字符串，如果出错则抛出错误"""
        try:
            data = json.loads(schema_bean.strip())
            if not isinstance(data, dict):
                raise
        except Exception as e:
            raise ValidateErrorException("传递数据必须符合OpenAPI规范的JSON字符串")
        return APIToolsBean(**data)

    def get_tool(self, provider_id: UUID, tool_name: str) -> ApiTool:
        account_id = "e2e2fd57-340d-4746-a89f-558fbca3af8f"
        print(type(provider_id), tool_name)
        data = self.db.session.query(ApiTool).filter_by(
            provider_id=provider_id,
            name=tool_name
        ).one_or_none()
        return data

    def get_tool_provider(self, provider_id: UUID):
        provider = self.get(ApiToolProvider, provider_id)
        account_id = "e2e2fd57-340d-4746-a89f-558fbca3af8f"
        if provider is None or str(provider.account_id) != account_id:
            raise ValidateErrorException("没有该提供信息")
        return provider

    def create_tool_provider(self, data: CreateApiToolSchema):
        path_schema = self.validator_schema(data.openapi_schema.data)
        account_id = uuid.uuid4()
        data_bean = self.db.session.query(ApiToolProvider).filter_by(
            name=data.name.data
        ).one_or_none()
        if data_bean:
            raise ValidateErrorException("服务提供商已存在")
        provider = self.create(ApiToolProvider,
                               account_id=account_id,
                               name=data.name.data,
                               icon=data.icon.data,
                               description=path_schema.description,
                               openapi_schema=data.openapi_schema.data,
                               headers=data.headers.data)

        for path, path_item in path_schema.paths.items():
            for method, method_item in path_item.items():
                self.create(
                    ApiTool,
                    account_id=account_id,
                    provider_id=provider.id,
                    name=method_item.get('operationId'),
                    description=method_item.get('description'),
                    method=method,
                    parameters=method_item.get('parameters', []),
                    url=f"{path_schema.server}{path}"
                )

    def delete_tool_provider(self, provider_id: UUID):
        provider = self.get(ApiToolProvider, provider_id)
        account_id = "e2e2fd57-340d-4746-a89f-558fbca3af8f"
        if provider is None or str(provider.account_id) != account_id:
            raise ValidateErrorException("该提供商不存在")
        with self.db.auto_commit():
            self.db.session.query(ApiTool).filter(
                ApiTool.provider_id == provider_id,
                ApiTool.account_id == account_id
            ).delete()
            self.db.session.delete(provider)
            self.db.session.flush()

    def get_api_tool_providers_with_page(self, req: GetApiToolProvidersWithPageReq) -> tuple[list[Any], Pageinator]:
        paginator = Pageinator(db=self.db, req=req)
        account_id = "e2e2fd57-340d-4746-a89f-558fbca3af8f"
        filter = []
        if req.search_word.data:
            filter.append(ApiToolProvider.name.ilike(f"%{req.search_word.data}%"))
        api_tool_providers = paginator.paginate(
            self.db.session.query(ApiToolProvider).filter(*filter).order_by(desc("created_at"))
        )
        return api_tool_providers, paginator

    def update_api_tool_provider(self, data: CreateApiToolSchema, provider_id: UUID):
        provider_val = self.db.session.query(ApiToolProvider).filter(
            ApiToolProvider.id != provider_id,
            ApiToolProvider.name == data.name.data
        )
        if provider_val is None:
            raise ValidateErrorException("该工具提供者不存在")
        provider = self.get(ApiToolProvider, provider_id)
        schema = self.validator_schema(data.openapi_schema.data)
        if provider is None:
            raise ValidateErrorException("提供商不存在")
        self.db.session.query(ApiTool).filter(ApiTool.provider_id == provider_id).delete()
        self.update(provider,
                    name=data.name.data,
                    icon=data.icon.data,
                    description=schema.description,
                    openapi_schema=data.openapi_schema.data,
                    headers=data.headers.data
                    )

        for path, path_item in schema.paths.items():
            for method, method_item in path_item.items():
                self.create(ApiTool,
                            account_id=provider.account_id,
                            provider_id=provider.id,
                            url=schema.server,
                            name=method_item.get('operationId'),
                            description=method_item.get('description'),
                            method=method,
                            parameters=method_item.get('parameters', []),
                            )

    def api_tool_invoke(self):
        tools = self.db.session.query(ApiTool).filter(
            ApiTool.provider_id == "433e2011-38a1-4608-a195-ccc900321849"
        ).one_or_none()
        tool = self.api_provider_manager.get_tool(ToolEntity(
            id=str(tools.provider_id),
            name=tools.name,
            url=tools.url,
            description=tools.description,
            parameters=tools.parameters,
        ))
        return tool.invoke({"q": "love", "doctype": "json"})
